#include "x11eventfilter.h"


#include <X11/Xlibint.h>

X11EventFilter::X11EventFilter(QObject *parent) :  QThread(parent)
  ,m_win(0)
  ,isPress(false)
{

}

void X11EventFilter::setWindow(unsigned long win)
{
    m_win = win;
}

void X11EventFilter::run()
{
    // 创建记录 XRecord 协议的 X 专用连接
    Display* display = XOpenDisplay(0);
    if (display == 0) {
        fprintf(stderr, "unable to open display\n");
        return;
    }

    // Receive from ALL clients, including future clients.
    // 初始化 XRecordCreateContext 所需的 XRecordClientSpec 参数
    // XRecordAllClients 的意思是 "记录所有 X Client" 的事件
    XRecordClientSpec clients = XRecordAllClients;
    // 创建 XRecordRange 变量，XRecordRange 用于控制记录事件的范围
    XRecordRange* range = XRecordAllocRange();
    if (range == 0) {
        fprintf(stderr, "unable to allocate XRecordRange\n");
        return;
    }

    // 初始化记录事件范围，范围开头设置成 KeyPress, 范围结尾设置成 MotionNotify 后
    // 事件的类型就包括 KeyPress、KeyRelase、ButtonPress、ButtonRelease、MotionNotify五种事件
    memset(range, 0, sizeof(XRecordRange));
    range->device_events.first = KeyPress;
    //range->device_events.last  = MotionNotify;
    range->device_events.last  = LASTEvent;

    // 根据上面的记录客户端类型和记录事件范围来创建　“记录上下文”
    // 然后把 XRecordContext 传递给 XRecordEnableContext 函数来开启事件记录循环
    XRecordContext context = XRecordCreateContext(display, 0, &clients, 1, &range, 1);
    if (context == 0) {
        fprintf(stderr, "XRecordCreateContext failed\n");
        return;
    }
    // 释放 range 指针
    XFree(range);

    // XSync 的作用就是把上面的 X 代码立即发给 X Server
    // 这样 X Server 接受到事件以后会立即发送给 XRecord 的 Client 连接
    XSync(display, True);

    // 建立一个专门读取 XRecord 协议数据的 X 链接
    Display* display_datalink = XOpenDisplay(0);
    if (display_datalink == 0) {
        fprintf(stderr, "unable to open second display\n");
        return;
    }

    // 调用 XRecordEnableContext 函数建立 XRecord 上下文
    // XRecordEnableContext 函数一旦调用就开始进入堵塞时的事件循环，直到线程或所属进程结束
    // X Server 事件一旦发生就传递给事件处理回调函数
    if (!XRecordEnableContext(display_datalink, context,  callback, (XPointer) this)) {
        fprintf(stderr, "XRecordEnableContext() failed\n");
        return;
    }
}

void X11EventFilter::callback(XPointer ptr, XRecordInterceptData* data)
{
    ((X11EventFilter *) ptr)->handleRecordEvent(data);
}

void X11EventFilter::handleRecordEvent(XRecordInterceptData* data)
{
    if (data->category == XRecordFromServer) {
        xEvent * event = (xEvent *)data->data;
        if (event->u.u.type != MotionNotify) qDebug() << "event type:" << event->u.u.type;
        switch (event->u.u.type) {
        case ButtonPress:
            if (filterWheelEvent(event->u.u.detail)) {
                isPress = true;
                emit buttonPress(
                    event->u.focus.window,
                    event->u.keyButtonPointer.rootX,
                    event->u.keyButtonPointer.rootY);
            }

            break;
        case MotionNotify:
            if (isPress) {
                emit buttonDrag(
                    event->u.focus.window,
                    event->u.keyButtonPointer.rootX,
                    event->u.keyButtonPointer.rootY);
            }

            break;
        case ButtonRelease:
            if (filterWheelEvent(event->u.u.detail)) {
                isPress = false;
                emit buttonRelease(
                    event->u.focus.window,
                    event->u.keyButtonPointer.rootX,
                    event->u.keyButtonPointer.rootY);
            }

            break;
        case KeyPress:
            emit keyPress(event->u.focus.window, ((unsigned char*) data->data)[1]);

            break;
        case KeyRelease:
            emit keyRelease(event->u.focus.window, ((unsigned char*) data->data)[1]);

            break;
        case ConfigureNotify:

            break;
        default:
            break;
        }
    }

    fflush(stdout);
    XRecordFreeData(data);
}

bool X11EventFilter::filterWheelEvent(int detail)
{
    return detail != WheelUp && detail != WheelDown && detail != WheelLeft && detail != WheelRight;
}
